/*  This file is part of -_-.

    -_- is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    -_- is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with -_-.  If not, see <http://www.gnu.org/licenses/>.

    Copyright 2012 Oliver Katz */

#include "kernel.h"

Event::Event()
{
	type = 0;
	key = 0;
}

Event Event::setKeyPress(int k)
{
	type = keypress;
	key = k;
	return *this;
}

Event Event::setKeyRelease(int k)
{
	type = keyrelease;
	key = k;
	return *this;
}

Event Event::setAxisMotion(int _minx, int _x, int _maxx, int _miny, int _y, int _maxy)
{
	type = axismotion;
	minx = _minx;
	x = _x;
	maxx = _maxx;
	miny = _miny;
	y = _y;
	maxy = _maxy;
	return *this;
}

Event Event::setButtonPress(int b)
{
	type = buttonpress;
	button = b;
	return *this;
}

Event Event::setButtonRelease(int b)
{
	type = buttonrelease;
	button = b;
	return *this;
}

int Event::setDevice(int id, string name)
{
	devid = id;
	devname = name;
	return 0;
}

int Event::getType()
{
	return type;
}

int Event::getKey()
{
	return key;
}

int Event::getMinX()
{
	return minx;
}

int Event::getX()
{
	return x;
}

int Event::getMaxX()
{
	return maxx;
}

int Event::getMinY()
{
	return miny;
}

int Event::getY()
{
	return y;
}

int Event::getMaxY()
{
	return maxy;
}

int Event::getButton()
{
	return button;
}

string Event::display()
{
	stringstream ss;
	ss << "(";
	if (type == keypress)
		ss << "KEYPRESS";
	else if (type == keyrelease)
		ss << "KEYRELEASE";
	else if (type == axismotion)
		ss << "AXISMOTION";
	else if (type == buttonpress)
		ss << "BUTTONPRESS";
	else if (type == buttonrelease)
		ss << "BUTTONRELEASE";
	else
		ss << "UNKNOWN";
	ss << " FROM " << devid << ":" << devname << " ";
	if (type == keypress || type == keyrelease)
		ss << "KEY " << key;
	else if (type == axismotion)
		ss << " X " << minx << "<" << x << "<" << maxx << " Y " << miny << "<" << y << "<" << maxy;
	else if (type == buttonpress || type == buttonrelease)
		ss << "BUTTON " << button;
	ss << ")";
	return ss.str();
}

DFBEnumerationResult enumerateInputDevice(unsigned int id, DFBInputDeviceDescription dsc, void *data)
{
	map<unsigned int, DFBInputDeviceDescription> *devs = (map<unsigned int, DFBInputDeviceDescription> *)data;
	(*devs)[id] = dsc;
	return DFENUM_OK;
}

Kernel::Kernel()
{
}

int Kernel::init(int *argc, char ***argv)
{
	DFBSurfaceDescription dsc;
	if (DirectFBInit(argc, argv) != DFB_OK)
		return 1;
	if (DirectFBCreate(&dfb) != DFB_OK)
		return 2;
	if (dfb->SetCooperativeLevel(dfb, DFSCL_FULLSCREEN) != DFB_OK)
		return 3;
	dsc.flags = (DFBSurfaceDescriptionFlags)(DSDESC_CAPS);
	dsc.caps = (DFBSurfaceCapabilities)(DSCAPS_PRIMARY | DSCAPS_FLIPPING);
	if (dfb->CreateSurface(dfb, &dsc, &pri) != DFB_OK)
		return 4;
	if (pri->GetSize(pri, &width, &height) != DFB_OK)
		return 5;
	if (dfb->EnumInputDevices(dfb, enumerateInputDevice, &devs) != DFB_OK)
		return 6;
	if (dfb->CreateInputEventBuffer(dfb, DICAPS_ALL, DFB_FALSE, &ebuf) != DFB_OK)
		return 7;
	return 0;
}

int Kernel::halt()
{
	ebuf->Release(ebuf);
	pri->Release(pri);
	dfb->Release(dfb);
	return 0;
}

int Kernel::getWidth()
{
	return width;
}

int Kernel::getHeight()
{
	return height;
}

int Kernel::rect(int x0, int y0, int x1, int y1, int r, int g, int b, int a)
{
	if (pri->SetColor(pri, r, g, b, a) != DFB_OK)
		return 1;
	if (pri->FillRectangle(pri, x0, y0, x1-x0, y1-y0) != DFB_OK)
		return 2;
	return 0;
}

int Kernel::line(int x0, int y0, int x1, int y1, int r, int g, int b, int a)
{
	if (pri->SetColor(pri, r, g, b, a) != DFB_OK)
		return 1;
	if (pri->DrawLine(pri, x0, y0, x1, y1) != DFB_OK)
		return 2;
	return 0;
}

int Kernel::imageGetSize(string path, int &w, int &h)
{
	IDirectFBImageProvider *pro;
	if (dfb->CreateImageProvider(dfb, path.c_str(), &pro) != DFB_OK)
		return 1;
	DFBSurfaceDescription dsc;
	if (pro->GetSurfaceDescription(pro, &dsc) != DFB_OK)
		return 2;
	w = dsc.width;
	h = dsc.height;
	pro->Release(pro);
	return 0;
}

int Kernel::image(int x, int y, string path)
{
	IDirectFBImageProvider *pro;
	IDirectFBSurface *surf;
	if (dfb->CreateImageProvider(dfb, path.c_str(), &pro) != DFB_OK)
		return 1;
	DFBSurfaceDescription dsc;
	if (pro->GetSurfaceDescription(pro, &dsc) != DFB_OK)
		return 2;
	if (dfb->CreateSurface(dfb, &dsc, &surf) != DFB_OK)
		return 3;
	if (pro->RenderTo(pro, surf, NULL) != DFB_OK)
		return 4;
	if (pri->Blit(pri, surf, NULL, x, y) != DFB_OK)
		return 5;
	surf->Release(surf);
	pro->Release(pro);
	return 0;
}

int Kernel::loadFont(string name, string path, int size)
{
	DFBFontDescription fdsc;
	fdsc.flags = DFDESC_HEIGHT;
	fdsc.height = size;
	IDirectFBFont *font;
	if (dfb->CreateFont(dfb, path.c_str(), &fdsc, &font) != DFB_OK)
		return 1;
	fonts[name] = font;
	return 0;
}

int Kernel::unloadFont(string name)
{
	fonts[name]->Release(fonts[name]);
}

int Kernel::getTextWidth(string font, string text)
{
	int w = -1;
	if (fonts[font]->GetStringWidth(fonts[font], text.c_str(), -1, &w) != DFB_OK)
		return -1;
	return w;
}

int Kernel::text(int x0, int y0, int r, int g, int b, int a, string font, string s)
{
	if (pri->SetFont(pri, fonts[font]) != DFB_OK)
		return 1;
	if (pri->SetColor(pri, r, g, b, a) != DFB_OK)
		return 2;
	if (pri->DrawString(pri, s.c_str(), -1, x0, y0, DSTF_LEFT) != DFB_OK)
		return 3;
	return 0;
}

int Kernel::update()
{
	if (pri->Flip(pri, NULL, (DFBSurfaceFlipFlags)0) != DFB_OK)
		return 1;
	return 0;
}

int Kernel::update(int x0, int y0, int x1, int y1)
{
	DFBRegion r = {
		x0,
		y0,
		x1,
		y1
	};

	if (pri->Flip(pri, &r, (DFBSurfaceFlipFlags)0) != DFB_OK)
		return 1;
	return 0;
}

vector<Event> Kernel::waitForEvents()
{
	vector<Event> rtn;
	DFBInputEvent e;
	if (ebuf->WaitForEvent(ebuf) != DFB_OK)
		return rtn;

	while(ebuf->GetEvent(ebuf, DFB_EVENT(&e)) == DFB_OK)
	{
		Event tmp;
		tmp.setDevice(e.device_id, devs[e.device_id].name);
		if (e.type == DIET_KEYRELEASE)
			tmp.setKeyRelease(e.key_symbol);
		else if (e.type == DIET_KEYPRESS)
			tmp.setKeyPress(e.key_symbol);
		else if (e.type == DIET_AXISMOTION)
		{
			if (e.flags & DIEF_AXISABS)
			{
				if (e.axis == DIAI_X)
					tmp.setAxisMotion(e.min, e.axisabs, e.max, 0, 0, 0);
				else if (e.axis == DIAI_Y)
					tmp.setAxisMotion(0, 0, 0, e.min, e.axisabs, e.max);
			}
			else if (e.flags & DIEF_AXISREL)
			{
				if (e.axis == DIAI_X)
					tmp.setAxisMotion(0, e.axisrel, 0, 0, 0, 0);
				else if (e.axis == DIAI_Y)
					tmp.setAxisMotion(0, 0, 0, 0, e.axisrel, 0);
			}
		}
		else if (e.type == DIET_BUTTONPRESS)
		{
			tmp.setButtonPress(e.button);
		}
		else if (e.type == DIET_BUTTONRELEASE)
		{
			tmp.setButtonRelease(e.button);
		}
		rtn.push_back(tmp);
	}

	return rtn;
}

IDirectFB *Kernel::getDFB()
{
	return dfb;
}

